/* ipp-usb - HTTP reverse proxy, backed by IPP-over-USB connection to device
 *
 * Copyright (C) 2020 and up by Alexander Pevzner (pzz@apevzner.com)
 * See LICENSE for license terms and conditions
 *
 * Device-specific quirks
 */

package ippusb

import (
	"fmt"
	"time"
)

// Quirks represents device-specific quirks
type Quirks struct {
	Origin           string            // file:line of definition
	Model            string            // Device model name
	Blacklist        bool              // Blacklist the device
	HTTPHeaders      map[string]string // HTTP header override
	UsbMaxInterfaces uint              // Max number of USB interfaces
	DisableFax       bool              // Disable fax for device
	ResetMethod      QuirksResetMethod // Device reset method
	InitDelay        time.Duration     // Delay before 1st IPP-USB request
	RequestDelay     time.Duration     // Delay between IPP-USB requests
	IgnoreIppStatus  bool              // Ignore IPP status
	Index            int               // Incremented in order of loading
}

var DefaultQuirks = Quirks{
	Origin:           "origin",
	Model:            "model",
	Blacklist:        false,
	HTTPHeaders:      map[string]string{"http-connection": ""},
	UsbMaxInterfaces: 0,
	DisableFax:       true,
	ResetMethod:      QuirksResetNone,
	//InitDelay:        ,
	//RequestDelay:     ,
	IgnoreIppStatus: false,
	Index:           0,
}

// QuirksResetMethod represents how to reset a device
// during initialization
type QuirksResetMethod int

// QuirksResetUnset - reset method not specified
// QuirksResetNone  - don't reset device at all
// QuirksResetSoft  - use class-specific soft reset
// QuirksResetHard  - use USB hard reset
const (
	QuirksResetUnset QuirksResetMethod = iota
	QuirksResetNone
	QuirksResetSoft
	QuirksResetHard
)

// String returns textual representation of QuirksResetMethod
func (m QuirksResetMethod) String() string {
	switch m {
	case QuirksResetUnset:
		return "unset"
	case QuirksResetNone:
		return "none"
	case QuirksResetSoft:
		return "soft"
	case QuirksResetHard:
		return "hard"
	}

	return fmt.Sprintf("unknown (%d)", int(m))
}

// empty returns true, if Quirks are actually empty
func (q *Quirks) empty() bool {
	return !q.Blacklist &&
		len(q.HTTPHeaders) == 0 &&
		q.UsbMaxInterfaces == 0 &&
		!q.DisableFax &&
		q.ResetMethod == QuirksResetUnset &&
		q.InitDelay == 0 &&
		q.RequestDelay == 0 &&
		!q.IgnoreIppStatus
}

// QuirksSet represents collection of quirks
type QuirksSet []*Quirks

// Add appends Quirks to QuirksSet
func (qset *QuirksSet) Add(q *Quirks) {
	*qset = append(*qset, q)
}

// ByModelName returns a subset of quirks, applicable for
// specific device, matched by model name
//
// In a case of multiple match, quirks are returned in
// the from most prioritized to least prioritized order
//
// Duplicates are removed: if some parameter is set by
// more prioritized entry, it is removed from the less
// prioritized entries. Entries, that in result become
// empty, are removed at all
func (qset QuirksSet) ByModelName(model string) QuirksSet {
	//type item struct {
	//	q        *Quirks
	//	matchlen int
	//}
	//var list []item
	//
	//// Get list of matching quirks
	//for _, q := range qset {
	//	matchlen := GlobMatch(model, q.Model)
	//	if matchlen >= 0 {
	//		list = append(list, item{q, matchlen})
	//	}
	//}
	//
	//// Sort the list by matchlen, in decreasing order
	//sort.Slice(list, func(i, j int) bool {
	//	if list[i].matchlen != list[j].matchlen {
	//		return list[i].matchlen > list[j].matchlen
	//	}
	//	return list[i].q.Index < list[j].q.Index
	//})

	// Rebuild it into the slice of *Quirks
	quirks := make(QuirksSet, 1)
	//for i := range list {
	//	quirks[i] = list[i].q
	//}
	//
	//// Remove duplicates and empty entries
	//httpHeaderSeen := make(map[string]struct{})
	//out := 0
	//for in, q := range quirks {
	//	// Note, here we avoid modification of the HTTPHeaders
	//	// map in the original Quirks structure
	//	//
	//	// Unfortunately, Golang misses immutable types,
	//	// so we must be very careful here
	//	q2 := &Quirks{}
	//	*q2 = *q
	//	q2.HTTPHeaders = make(map[string]string)
	//
	//	for name, value := range quirks[in].HTTPHeaders {
	//		if _, seen := httpHeaderSeen[name]; !seen {
	//			httpHeaderSeen[name] = struct{}{}
	//			q2.HTTPHeaders[name] = value
	//		}
	//	}
	//
	//	if !q2.empty() {
	//		quirks[out] = q2
	//		out++
	//	}
	//}

	quirks[0] = &DefaultQuirks

	return quirks
}

// GetBlacklist returns effective Blacklist parameter,
// taking the whole set into consideration
func (qset QuirksSet) GetBlacklist() bool {
	for _, q := range qset {
		if q.Blacklist {
			return true
		}
	}

	return false
}

// GetUsbMaxInterfaces returns effective UsbMaxInterfaces parameter,
// taking the whole set into consideration
func (qset QuirksSet) GetUsbMaxInterfaces() uint {
	for _, q := range qset {
		if q.UsbMaxInterfaces != 0 {
			return q.UsbMaxInterfaces
		}
	}

	return 0
}

// GetDisableFax returns effective DisableFax parameter,
// taking the whole set into consideration
func (qset QuirksSet) GetDisableFax() bool {
	for _, q := range qset {
		if q.DisableFax {
			return true
		}
	}

	return false
}

// GetResetMethod returns effective ResetMethod parameter
func (qset QuirksSet) GetResetMethod() QuirksResetMethod {
	for _, q := range qset {
		if q.ResetMethod != QuirksResetUnset {
			return q.ResetMethod
		}
	}

	return QuirksResetNone
}

// GetInitDelay returns effective InitDelay parameter
func (qset QuirksSet) GetInitDelay() time.Duration {
	for _, q := range qset {
		if q.InitDelay != 0 {
			return q.InitDelay
		}
	}

	return 0
}

// GetRequestDelay returns effective RequestDelay parameter
func (qset QuirksSet) GetRequestDelay() time.Duration {
	for _, q := range qset {
		if q.RequestDelay != 0 {
			return q.RequestDelay
		}
	}

	return 0
}

// GetIgnoreIppStatus returns effective IgnoreIppStatus parameter,
// taking the whole set into consideration
func (qset QuirksSet) GetIgnoreIppStatus() bool {
	for _, q := range qset {
		if q.IgnoreIppStatus {
			return true
		}
	}

	return false
}
